package main

import (
	"context"
	"fmt"
	"net"
	"os"

	"atomgit.com/openlinksaas/easy_trace/config"
	"atomgit.com/openlinksaas/easy_trace/data_store"
	"atomgit.com/openlinksaas/easy_trace/serv_impl"
	"atomgit.com/openlinksaas/proto-gen-go.git/trace_proxy_api"
	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/jaegerreceiver"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/skywalkingreceiver"
	"github.com/open-telemetry/opentelemetry-collector-contrib/receiver/zipkinreceiver"
	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/config/configgrpc"
	"go.opentelemetry.io/collector/config/confighttp"
	"go.opentelemetry.io/collector/config/confignet"
	"go.opentelemetry.io/collector/config/configtelemetry"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/receiver"
	"go.opentelemetry.io/collector/receiver/otlpreceiver"
	noopmetric "go.opentelemetry.io/otel/metric/noop"
	nooptrace "go.opentelemetry.io/otel/trace/noop"
	"go.uber.org/zap"
	"google.golang.org/grpc"
)

func createSettings() receiver.CreateSettings {
	return receiver.CreateSettings{
		TelemetrySettings: component.TelemetrySettings{
			Logger:         zap.NewNop(),
			TracerProvider: nooptrace.NewTracerProvider(),
			MeterProvider:  noopmetric.NewMeterProvider(),
			MetricsLevel:   configtelemetry.LevelNone,
			Resource:       pcommon.NewResource(),
			ReportComponentStatus: func(*component.StatusEvent) error {
				return nil
			},
		},
		BuildInfo: component.NewDefaultBuildInfo(),
	}
}

func startZipkinReciver(cfg *config.ZipkinConfig, ds *data_store.DataStore) error {
	factory := zipkinreceiver.NewFactory()
	recvCfg := factory.CreateDefaultConfig().(*zipkinreceiver.Config)
	recvCfg.HTTPServerSettings = confighttp.HTTPServerSettings{
		Endpoint: cfg.ListenAddr,
	}
	recvCfg.ParseStringTags = false

	setting := createSettings()

	recvImpl, err := factory.CreateTracesReceiver(context.Background(), setting, recvCfg, ds)
	if err != nil {
		return err
	}

	err = recvImpl.Start(context.Background(), &nopHost{})
	if err != nil {
		return err
	}

	return nil
}

func startJaegerReciver(cfg *config.JaegerConfig, ds *data_store.DataStore) error {
	factory := jaegerreceiver.NewFactory()
	recvCfg := factory.CreateDefaultConfig().(*jaegerreceiver.Config)
	recvCfg.Protocols.GRPC = &configgrpc.GRPCServerSettings{
		NetAddr: confignet.NetAddr{
			Endpoint:  cfg.GrpcListenAddr,
			Transport: "tcp",
		},
	}
	recvCfg.Protocols.ThriftHTTP = &confighttp.HTTPServerSettings{
		Endpoint: cfg.HttpListenAddr,
	}
	recvCfg.Protocols.ThriftCompact = &jaegerreceiver.ProtocolUDP{
		Endpoint: cfg.ThriftCompactListenAddr,
		ServerConfigUDP: jaegerreceiver.ServerConfigUDP{
			QueueSize:        1_000,
			MaxPacketSize:    65_536,
			Workers:          5,
			SocketBufferSize: 0,
		},
	}
	recvCfg.Protocols.ThriftBinary = &jaegerreceiver.ProtocolUDP{
		Endpoint: cfg.ThriftBinaryListenAddr,
		ServerConfigUDP: jaegerreceiver.ServerConfigUDP{
			QueueSize:        1_000,
			MaxPacketSize:    65_536,
			Workers:          5,
			SocketBufferSize: 0,
		},
	}

	setting := createSettings()

	recvImpl, err := factory.CreateTracesReceiver(context.Background(), setting, recvCfg, ds)
	if err != nil {
		return err
	}

	err = recvImpl.Start(context.Background(), &nopHost{})
	if err != nil {
		return err
	}

	return nil
}

func startSkywalkingReciver(cfg *config.SkywalkingConfig, ds *data_store.DataStore) error {
	factory := skywalkingreceiver.NewFactory()
	recvCfg := factory.CreateDefaultConfig().(*skywalkingreceiver.Config)

	recvCfg.GRPC = &configgrpc.GRPCServerSettings{
		NetAddr: confignet.NetAddr{
			Endpoint:  cfg.GrpcListenAddr,
			Transport: "tcp",
		},
	}
	recvCfg.HTTP = &confighttp.HTTPServerSettings{
		Endpoint: cfg.HttpListenAddr,
	}

	setting := createSettings()

	recvImpl, err := factory.CreateTracesReceiver(context.Background(), setting, recvCfg, ds)
	if err != nil {
		return err
	}

	err = recvImpl.Start(context.Background(), &nopHost{})
	if err != nil {
		return err
	}
	return nil
}

func startOltpReciver(cfg *config.OtlpConfig, ds *data_store.DataStore) error {
	factory := otlpreceiver.NewFactory()
	recvCfg := factory.CreateDefaultConfig().(*otlpreceiver.Config)
	recvCfg.Protocols.GRPC.NetAddr.Endpoint = cfg.GrpcListenAddr
	recvCfg.Protocols.HTTP.HTTPServerSettings.Endpoint = cfg.HttpListenAddr

	setting := createSettings()

	recvImpl, err := factory.CreateTracesReceiver(context.Background(), setting, recvCfg, ds)
	if err != nil {
		return err
	}

	err = recvImpl.Start(context.Background(), &nopHost{})
	if err != nil {
		return err
	}
	return nil
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("Usage:%s [config file]\n", os.Args[0])
		return
	}
	cfg, err := config.LoadServerCfg(os.Args[1])
	if err != nil {
		fmt.Println(err)
		return
	}
	ds := data_store.NewDataStore(cfg.MaxTraceStore)
	if cfg.Zipkin.Enable {
		err = startZipkinReciver(&cfg.Zipkin, ds)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	if cfg.Jaeger.Enable {
		err = startJaegerReciver(&cfg.Jaeger, ds)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	if cfg.Skywalking.Enable {
		err = startSkywalkingReciver(&cfg.Skywalking, ds)
		if err != nil {
			fmt.Println(err)
			return
		}
	}
	if cfg.Otlp.Enable {
		err = startOltpReciver(&cfg.Otlp, ds)
		if err != nil {
			fmt.Println(err)
			return
		}
	}

	//启动服务
	grpcServer := grpc.NewServer(grpc.StreamInterceptor(
		grpc_middleware.ChainStreamServer(
			grpc_recovery.StreamServerInterceptor(),
		)),
		grpc.UnaryInterceptor(
			grpc_middleware.ChainUnaryServer(
				grpc_recovery.UnaryServerInterceptor(),
			)),
	)
	trace_proxy_api.RegisterTraceProxyApiServer(grpcServer, serv_impl.NewTraceProxyApiImpl(ds, os.Args[1]))
	addr, err := net.ResolveTCPAddr("tcp4", cfg.ListenAddr)
	if err != nil {
		fmt.Println(err)
		return
	}
	li, err := net.ListenTCP("tcp4", addr)
	if err != nil {
		fmt.Println(err)
		return
	}
	err = grpcServer.Serve(li)
	if err != nil {
		fmt.Println(err)
		return
	}
}
